import tkinter as tk
import threading
import asyncio
import signal
import sys
import os
from ui_components import PortScannerUI
from scanner import PortScanner
from message_dialog import MessageDialog
from permission_checker import PermissionChecker


class PortScannerApp:
    """端口扫描器应用程序控制器"""

    def __init__(self):
        self.root = tk.Tk()
        self.scanner = PortScanner()
        self.ui = PortScannerUI(self.root, self.start_scan, self.cancel_scan)
        self.message_dialog = MessageDialog(self.root)
        self.permission_checker = PermissionChecker()
        self.root.title("系统端口占用检查工具")
        self.root.geometry("900x700")
        self.root.resizable(True, True)

        # 设置窗口图标
        self._set_window_icon()

        # 扫描状态
        self.scan_thread = None

        # 设置信号处理和窗口关闭事件
        self._setup_signal_handlers()

        # 启动时进行权限检查
        self._initial_permission_check()

    def _set_window_icon(self):
        """设置窗口图标"""
        try:
            # 获取当前脚本所在目录
            current_dir = os.path.dirname(os.path.abspath(__file__))

            # 尝试多个可能的图标路径
            # 在_set_window_icon方法中添加自定义路径
            icon_paths = [
                os.path.join(current_dir, "assets", "port_scanner_icon.ico"),
                os.path.join(current_dir, "icon.ico"),
                os.path.join(current_dir, "assets", "app_icon.ico"),
                os.path.join(current_dir, "app_icon.ico"),
            ]

            # 查找存在的图标文件
            for icon_path in icon_paths:
                if os.path.exists(icon_path):
                    self.root.iconbitmap(icon_path)
                    # print(f"已设置窗口图标: {icon_path}")
                    return

            # 如果没有找到ico文件，尝试使用png文件（需要PIL库）
            png_paths = [
                os.path.join(current_dir, "assets", "icon.png"),
                os.path.join(current_dir, "icon.png"),
                os.path.join(current_dir, "assets", "app_icon.png"),
                os.path.join(current_dir, "app_icon.png"),
            ]

            for png_path in png_paths:
                if os.path.exists(png_path):
                    try:
                        from PIL import Image, ImageTk

                        # 加载PNG图像并转换为PhotoImage
                        image = Image.open(png_path)
                        # 调整图标大小（通常32x32或16x16）
                        image = image.resize((32, 32), Image.Resampling.LANCZOS)
                        photo = ImageTk.PhotoImage(image)
                        self.root.iconphoto(True, photo)
                        # print(f"已设置窗口图标: {png_path}")
                        return
                    except ImportError:
                        print("警告: 需要安装PIL库来支持PNG图标格式")
                        print("请运行: pip install Pillow")
                        continue
                    except Exception as e:
                        print(f"设置PNG图标失败: {e}")
                        continue

            print("警告: 未找到图标文件，使用默认图标")
            print("建议在以下位置放置图标文件:")
            for path in icon_paths:
                print(f"  - {path}")

        except Exception as e:
            print(f"设置窗口图标时出错: {e}")

    def _setup_signal_handlers(self):
        """设置信号处理"""
        signal.signal(signal.SIGINT, self._signal_handler)
        self.root.protocol("WM_DELETE_WINDOW", self._on_closing)

    def _signal_handler(self, signum, frame):
        """处理中断信号"""
        print("\n检测到中断信号，正在安全退出...")
        self._on_closing()

    def _on_closing(self):
        """处理窗口关闭事件"""
        if self.ui.scanning:
            self.cancel_scan()

            # 等待扫描线程结束
            if self.scan_thread and self.scan_thread.is_alive():
                self.scan_thread.join(timeout=2)

        try:
            self.root.quit()
            self.root.destroy()
        except:
            pass

        sys.exit(0)

    def _initial_permission_check(self):
        """启动时的初始权限检查"""
        try:
            # 检查基本权限
            result = self.permission_checker.check_required_permissions("host")

            if result["warnings"] or result["errors"]:
                # 显示权限检查结果
                summary = self.permission_checker.get_permission_summary("host")
                self.ui.show_message(
                    "权限检查",
                    f"权限检查完成:\n\n{summary}",
                    "warning"
                    if result["warnings"] and not result["errors"]
                    else "info",
                    width=400,
                    height=400,
                )

        except Exception as e:
            print(f"初始权限检查失败: {e}")

    def start_scan(self):
        """开始扫描"""
        try:
            # 获取扫描参数
            params = self.ui.get_scan_params()

            # 验证参数
            if params["start_port"] > params["end_port"]:
                self.ui.show_message("警告", "起始端口不能大于结束端口", "warning")
                return

            if params["start_port"] < 1 or params["end_port"] > 65535:
                self.ui.show_message("警告", "端口范围必须在1-65535之间", "warning")
                return

            # 设置UI状态
            self.ui.set_scanning_state(True)
            self.ui.set_status("扫描中...")
            self.ui._clear_results()

            # 重置扫描器状态
            self.scanner.set_cancel_flag(False)

            # 在新线程中运行异步扫描
            self.scan_thread = threading.Thread(
                target=self._run_async_scan, args=(params,)
            )
            self.scan_thread.daemon = True
            self.scan_thread.start()

        except ValueError:
            self.ui.show_message("错误", "请输入有效的数字", "error")
        except Exception as e:
            self.ui.show_message("错误", f"启动扫描时出错: {str(e)}", "error")

    def cancel_scan(self):
        """取消扫描"""
        self.scanner.set_cancel_flag(True)
        self.ui.set_status("正在取消扫描...")

    def _run_async_scan(self, params: dict):
        """运行异步扫描"""
        was_cancelled = False
        try:
            # 创建新的事件循环
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)

            # 运行扫描
            if params["env"] == "host":
                results = loop.run_until_complete(
                    self.scanner.scan_host_ports(
                        params["host"],
                        params["start_port"],
                        params["end_port"],
                        self._progress_callback,
                        self._result_callback,  # 添加结果回调
                    )
                )
            elif params["env"] == "wsl":
                results = loop.run_until_complete(
                    self.scanner.scan_wsl_ports(
                        params["wsl_distro"],
                        params["host"],
                        params["start_port"],
                        params["end_port"],
                        self._progress_callback,
                        self._result_callback,  # 添加结果回调
                    )
                )
            elif params["env"] == "docker":
                results = loop.run_until_complete(
                    self.scanner.scan_docker_ports(
                        params["docker_container"],
                        params["start_port"],
                        params["end_port"],
                        self._progress_callback,
                        self._result_callback,  # 添加结果回调
                    )
                )

            # 检查是否被取消
            was_cancelled = self.scanner.cancel_scan

            # 根据是否被取消显示不同的完成消息
            if was_cancelled:
                self.root.after(0, self._scan_cancelled)
            else:
                self.root.after(0, self._scan_complete)

        except ValueError as e:
            error_msg = str(e)
            self.root.after(0, lambda: self.ui.show_message("错误", error_msg, "error"))
            self.root.after(0, self._scan_complete)
        except Exception as e:
            error_msg = str(e)
            self.root.after(
                0,
                lambda: self.ui.show_message(
                    "错误", f"扫描过程中发生错误: {error_msg}", "error"
                ),
            )
            self.root.after(0, self._scan_complete)
        finally:
            loop.close()

    def _progress_callback(self, completed: int, total: int, current_port: int):
        """进度回调"""
        self.root.after(0, self.ui.update_progress, completed, total, current_port)

    def _result_callback(self, result: dict):
        """结果回调 - 实时显示扫描结果"""
        self.root.after(
            0,
            self.ui.add_result,
            result["port"],
            result["status"],
            result["service"],
            result["process"],
        )

    def _scan_complete(self):
        """扫描完成处理"""
        self.ui.set_scanning_state(False)
        self.ui.set_status("扫描完成")
        self.message_dialog.show("扫描完成", "扫描完成！", "success")

    def _scan_cancelled(self):
        """扫描取消处理"""
        self.ui.set_scanning_state(False)
        self.ui.set_status("扫描已取消")
        self.message_dialog.show("扫描取消", "扫描已被用户取消", "warning")

    def run(self):
        """运行应用程序"""
        self.root.mainloop()


def main():
    """主函数"""
    try:
        app = PortScannerApp()
        app.run()
    except KeyboardInterrupt:
        print("\n程序被用户中断")
        sys.exit(0)
    except Exception as e:
        print(f"程序运行出错: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
